#
# LAB/SysDIO.pm
#
# Copyright (C) 2004 Albacore Technology, LLC All Rights Reserved
#
#

package LAB::SysDIO;

use strict;
use Cache::FileCache;

use vars    qw
(
  $VERSION @ISA @EXPORT @EXPORT_OK
  %return_labels     @return_values
  %type_labels       @type_values
  %repl_labels       @repl_values
  %inkey_type_labels @inkey_type_values
  %action_labels     @action_values
  %cache_type_labels @cache_type_values
  %cache_exp_labels  @cache_exp_values
  %audit_type_labels @audit_type_values
);

use Exporter;
$VERSION = 0.01;
@ISA     = qw(Exporter);
@EXPORT  = qw
(
  get_dio_profiles
);

@EXPORT_OK =  qw();

# globals

@inkey_type_values = ( '', 'tag_based', 'use_default' );
%inkey_type_labels = (
  ''            => '-------',
  'tag_based'   => 'Tag Based',
  'use_default' => 'Use Default'
);

@return_values = ('','scalar','hash','hashofscalar','hashofhash','hashofarray',
                  'arrayofhash','array','arrayofscalar','arrayofarray','none',
                  'hashiter','arrayiter','scalariter','dropdown');
%return_labels = (
  ''              => '-------',
  'scalar'        => 'scalar',
  'hash'          => 'hash',
  'hashofscalar'  => 'hashofscalar',
  'hashofhash'    => 'hashofhash',
  'hashofarray'   => 'hashofarray',
  'arrayofhash'   => 'arrayofhash',
  'array'         => 'array',
  'arrayofscalar' => 'arrayofscalar',
  'arrayofarray'  => 'arrayofarray',
  'none'          => 'none',
  'hashiter'      => 'hashiter',
  'arrayiter'     => 'arrayiter',
  'scalariter'    => 'scalariter',
  'dropdown'      => 'dropdown',
);

@type_values = ('DBI','DBI-Iter','DBI-Factory','DBI-Factory-Prep', 'SQL::Abstract','SOAP');
%type_labels = (
  'DBI'              => 'DBI',
  'DBI-Iter'         => 'DBI-Iter',
  'DBI-Factory'      => 'DBI-Factory',
  'DBI-Factory-Prep' => 'DBI-Factory-Prep',
  'SQL::Abstract'    => 'SQL::Abstract',
  'SOAP'             => 'SOAP',
);

@repl_values = ('','none','one-way','two-way');
%repl_labels = (
  ''        => '',
  'none'    => 'none',
  'one-way' => 'one-way',
  'two-way' => 'two-way',
);

@action_values = ('','select','insert','update','delete');
%action_labels = (
  ''       => '',
  'select' => 'select',
  'insert' => 'insert',
  'update' => 'update',
  'delete' => 'delete',
);

@cache_type_values = ('','m','f');
%cache_type_labels = (
  ''  => '',
  'm' => 'Memory',
  'f' => 'File',
);

@cache_exp_values = ('','never','second','minute','hour','day','week','month',
                     'year');
%cache_exp_labels = (
  ''       => '',
  'never'  => 'never',
  'second' => 'second',
  'minute' => 'minute',
  'hour'   => 'hour',
  'day'    => 'day',
  'week'   => 'week',
  'month'  => 'month',
  'year'   => 'year',
);

@audit_type_values = ('','A','D','R');
%audit_type_labels = (
  ''  => '',
  'A' => 'Audit',
  'D' => 'Audit w/Data',
  'R' => 'Audit w/Data & Results',
);

#
# Global Funcs
#
sub get_dio_profiles {
  my ($dio) = @_;

  my $items;

  my $pcache = new Cache::FileCache( {'namespace' => 'LSL_dio_profile'});

  foreach my $key ($pcache->get_keys()) {
    next if $dio ne $key && $dio ne '';       # only grab specified or all
    # http://www.bagley.org/~doug/shootout/
    my $dio_h = $pcache->get($key);
    my @nums;
    my ($sum, $min, $max) = (0, 99999999, 0);
    foreach my $t (keys %{ $dio_h->{'time'} }) {
      foreach my $dio (@{ $dio_h->{'time'}{$t} }) {
        my $diff = $dio - $t;
        push @nums, $diff;
        $sum += $diff;
        $min = $diff if $diff < $min;
        $max = $diff if $diff > $max;
      }
      my $n = scalar(@nums);
      my $mean = $sum/$n;
      my $average_deviation = 0;
      my $standard_deviation = 0;
      my $variance = 0;
      my $skew = 0;
      my $kurtosis = 0;
      foreach (@nums) {
          my $deviation = $_ - $mean;
          $average_deviation += abs($deviation);
          $variance += $deviation**2;
          $skew += $deviation**3;
          $kurtosis += $deviation**4;
      }
      $average_deviation /= $n;
      $variance /= ($n - 1) if $n > 1;
      $standard_deviation = sqrt($variance);
      if ($variance) {
          $skew /= ($n * $variance * $standard_deviation);
          $kurtosis = $kurtosis/($n * $variance * $variance) - 3.0;
      }
      @nums = sort { $a <=> $b } @nums;
      my $mid = int($n/2);
      my $median = ($n % 2) ? $nums[$mid] : ($nums[$mid] + $nums[$mid-1])/2;
      #
      $items->{$key}{'stats'}{'n'}                  = $n;
      $items->{$key}{'stats'}{'max'}                = $max;
      $items->{$key}{'stats'}{'min'}                = $min;
      $items->{$key}{'stats'}{'median'}             = $median;
      $items->{$key}{'stats'}{'mean'}               = $mean;
      $items->{$key}{'stats'}{'average_deviation'}  = $average_deviation;
      $items->{$key}{'stats'}{'standard_deviation'} = $standard_deviation;
      $items->{$key}{'stats'}{'variance'}           = $variance;
      $items->{$key}{'stats'}{'skew'}               = $skew;
      $items->{$key}{'stats'}{'kurtosis'}           = $kurtosis;
    }
  }

  return $items;
}

#
# Object Funcs
#
# begin object
sub start_perf {
  my ($class, $object, $name, $args) = @_;

  my $pcache = new Cache::FileCache( {'namespace' => $object . '_profile'});

  my $pdata = $pcache->get($name);

  my $t = time();

  push @{ $pdata->{'args'}{$t} }, $args if $args;

  my $self = {
    'pcache' => $pcache,
    'pdata'  => $pdata,
    'name'   => $name,
    't'      => $t,
  };

  bless ( $self, $class );

  return ($self);
}
sub end_perf {
  my ($self) = @_;

  push @{ $self->{'pdata'}->{'time'}{$self->{'t'}} }, time();

  $self->{'pcache'}->set($self->{'name'}, $self->{'pdata'});
}

1;
